#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include <pthread.h>

#include <raylib.h>

#include "common.h"
#include "log.h"

#include "ar.h"
#include "extractor-gui.h"

#define COLOR_BACKGROUND GetColor(0x151515FF)
#define COLOR_PROGRESSBAR_BACKGROUND GetColor(0x333333FF)
#define COLOR_TEXT_ERROR    RED
#define COLOR_TEXT_PROGRESS BLUE
#define COLOR_TEXT_SUCCESS  GREEN

#define FONT_SIZE 20

#define PROGRESSBAR_HEIGHT 20
#define PROGRESSBAR_PADDING 20

#define TEXT_POS_X PROGRESSBAR_PADDING
#define TEXT_POS_Y 50

typedef struct {
    Rectangle inner;
    Rectangle outer;
    bool flipped;
} Progressbar;

typedef struct {
    AR *ar;
    Progressbar progressbar;
    pthread_t ptid;
    bool extraction_started;
} RaylibExtractor;

static RaylibExtractor *rl_ex;

static void extracting_screen(void);
static void extracted_screen(void);

bool extractor_gui_init(const char *source, const char *dest, const AROptions *options)
{
    float screenWidth;
    float screenHeight;

    SetConfigFlags(FLAG_VSYNC_HINT);
    InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "ex");
    SetExitKey(KEY_NULL);

    screenWidth = (float)GetScreenWidth();
    screenHeight = (float)GetScreenHeight();

    rl_ex = malloc(sizeof(*rl_ex));
    assert(rl_ex != NULL && "Out of memory");
    memset(rl_ex, 0, sizeof(*rl_ex));

    rl_ex->ar = ar_init_extractor(source, dest, options);
    rl_ex->extraction_started = false;

    if (rl_ex->ar == NULL) {
        return false;
    }

    rl_ex->progressbar.outer.x = PROGRESSBAR_PADDING;
    rl_ex->progressbar.outer.y = screenHeight/2 - (PROGRESSBAR_HEIGHT / 2);
    rl_ex->progressbar.outer.width = screenWidth - (PROGRESSBAR_PADDING * 2);
    rl_ex->progressbar.outer.height = PROGRESSBAR_HEIGHT;

    rl_ex->progressbar.inner.x = PROGRESSBAR_PADDING;
    rl_ex->progressbar.inner.y = rl_ex->progressbar.outer.y;
    rl_ex->progressbar.inner.width = rl_ex->progressbar.outer.width / 10;
    rl_ex->progressbar.inner.height = rl_ex->progressbar.outer.height;

    rl_ex->progressbar.flipped = false;
    return true;
}

void extractor_gui_run(void)
{
    while (!WindowShouldClose()) {
        if (false == rl_ex->extraction_started) {
            rl_ex->extraction_started = true;
            pthread_create(&rl_ex->ptid, NULL, (void *(*)(void*))&ar_decompress_archive, rl_ex->ar);
            pthread_detach(rl_ex->ptid);
        }

        BeginDrawing();
        ClearBackground(COLOR_BACKGROUND);

        if (false == ar_check_complete(rl_ex->ar)) {
            extracting_screen();
        } else {
            extracted_screen();
        }

        EndDrawing();
    }
}

void extractor_gui_deinit(void)
{
    free(rl_ex);
}

static void extracted_screen(void)
{
    DrawText("Extracted!", TEXT_POS_X, TEXT_POS_Y, FONT_SIZE, COLOR_TEXT_SUCCESS);
    DrawRectangleRounded(rl_ex->progressbar.outer, 0.2f, 5,
                         COLOR_PROGRESSBAR_BACKGROUND);

    rl_ex->progressbar.inner.x = rl_ex->progressbar.outer.x;
    rl_ex->progressbar.inner.width = rl_ex->progressbar.outer.width;
    DrawRectangleRounded(rl_ex->progressbar.inner, 0.2f, 5, COLOR_TEXT_SUCCESS);

    SetExitKey(KEY_ESCAPE);
}

static void extracting_screen(void)
{
    float speed;
    float xBounds;

    DrawText("Extracting...", TEXT_POS_X, TEXT_POS_Y, FONT_SIZE,
             COLOR_TEXT_PROGRESS);
    DrawRectangleRounded(rl_ex->progressbar.outer, 0.2f, 5,
                         COLOR_PROGRESSBAR_BACKGROUND);

    speed = rl_ex->progressbar.flipped ? -2 : 2;
    rl_ex->progressbar.inner.x += speed;

    xBounds = rl_ex->progressbar.outer.x + rl_ex->progressbar.outer.width;
    if (rl_ex->progressbar.inner.x >= xBounds - rl_ex->progressbar.inner.width) {
        rl_ex->progressbar.flipped = true;
    }
    if (rl_ex->progressbar.inner.x <= rl_ex->progressbar.outer.x) {
        rl_ex->progressbar.flipped = false;
    }
    DrawRectangleRounded(rl_ex->progressbar.inner, 0.2f, 5, COLOR_TEXT_PROGRESS);
}
